#ifndef XG_JSON_H
#define XG_JSON_H
////////////////////////////////////////////////////////////////////
extern "C"
{
	#include "src/cJSON.h"
}

#include "../stdx/Reflect.h"

class JsonElement : public Object
{
public:
	class iterator
	{
		friend class JsonElement;

	private:
		int idx;
		int len;
		cJSON* elem;
		sp<cJSON> holder;

		iterator(cJSON* item, JsonElement* parent);

	public:
		iterator& operator ++ ();
		JsonElement operator * ();
		bool operator != (const iterator& obj) const;
	};

protected:
	cJSON* elem;
	sp<cJSON> holder;

	static bool InitElement(cJSON* item);
	static bool InitElement(cJSON* item, int val);
	static bool InitElement(cJSON* item, bool val);
	static bool InitElement(cJSON* item, long val);
	static bool InitElement(cJSON* item, double val);
	static bool InitElement(cJSON* item, const char* val);

public:
	int size() const;
	JsonElement add();
	void remove(int idx);
	string toString() const;
	bool load(const string& msg);
	JsonElement get(int idx) const;
	void remove(const string& name);
	bool init(const string& msg = "");
	JsonElement add(const string& name);
	JsonElement addArray(const string& name);
	JsonElement get(const string& name) const;

	JsonElement()
	{
		elem = NULL;
	}
	JsonElement(const string& msg)
	{
		elem = NULL;
		init(msg);
	}
	JsonElement(cJSON* item, const JsonElement* parent)
	{
		holder = parent ? parent->holder : NULL;
		elem = item;
	}

public:
	iterator end()
	{
		return iterator(NULL, this);
	}
	iterator begin()
	{
		return iterator(elem, this);
	}
	string getName() const
	{
		return elem ? elem->string : stdx::EmptyString();
	}
	JsonElement pack(cJSON* item) const
	{
		return JsonElement(item, this);
	}

public:
	bool isNull() const
	{
		return elem == NULL || elem->type == cJSON_NULL;
	}
	bool isArray() const
	{
		return elem && elem->type == cJSON_Array;
	}
	bool isObject() const
	{
		return elem && elem->type == cJSON_Object;
	}
	bool isNumber() const
	{
		return elem && elem->type == cJSON_Number;
	}
	bool isString() const
	{
		return elem && elem->type == cJSON_String;
	}
	bool isBoolean() const
	{
		return elem && (elem->type == cJSON_True || elem->type == cJSON_False);
	}
	bool isNull(int idx) const
	{
		return get(idx).isNull();
	}
	bool isArray(int idx) const
	{
		return get(idx).isArray();
	}
	bool isObject(int idx) const
	{
		return get(idx).isObject();
	}
	bool isNumber(int idx) const
	{
		return get(idx).isNumber();
	}
	bool isString(int idx) const
	{
		return get(idx).isString();
	}
	bool isBoolean(int idx) const
	{
		return get(idx).isBoolean();
	}
	bool isNull(const string& name) const
	{
		return get(name).isNull();
	}
	bool isArray(const string& name) const
	{
		return get(name).isArray();
	}
	bool isObject(const string& name) const
	{
		return get(name).isObject();
	}
	bool isNumber(const string& name) const
	{
		return get(name).isNumber();
	}
	bool isString(const string& name) const
	{
		return get(name).isString();
	}
	bool isBoolean(const string& name) const
	{
		return get(name).isBoolean();
	}

public:
	bool setVariable(int val)
	{
		return InitElement(elem, val);
	}
	bool setVariable(bool val)
	{
		return InitElement(elem, val);
	}
	bool setVariable(long val)
	{
		return InitElement(elem, val);
	}
	bool setVariable(double val)
	{
		return InitElement(elem, val);
	}
	bool setVariable(const char* val)
	{
		return InitElement(elem, val);
	}
	bool setVariable(const string& val)
	{
		return InitElement(elem, val.c_str());
	}

	string getVariable() const
	{
		if (elem == NULL) return stdx::EmptyString();
		if (elem->type == cJSON_True) return "true";
		if (elem->type == cJSON_False) return "false";
		if (elem->type == cJSON_Number) return stdx::str(elem->valuedouble);
		return elem->valuestring ? elem->valuestring : stdx::EmptyString();
	}
	bool getVariable(bool& val) const
	{
		CHECK_FALSE_RETURN(elem && (elem->type == cJSON_True || elem->type == cJSON_False));
		val = elem->type == cJSON_True;
		return true;
	}
	bool getVariable(int& val) const
	{
		CHECK_FALSE_RETURN(elem && elem->type == cJSON_Number);
		val = elem->valueint;
		return true;
	}
	bool getVariable(long& val) const
	{
		CHECK_FALSE_RETURN(elem && elem->type == cJSON_Number);
		val = elem->valueint;
		return true;
	}
	bool getVariable(double& val) const
	{
		CHECK_FALSE_RETURN(elem && elem->type == cJSON_Number);
		val = elem->valuedouble;
		return true;
	}
	bool getVariable(string& val) const
	{
		CHECK_FALSE_RETURN(elem && elem->valuestring);
		val = elem->valuestring;
		return true;
	}

	long asLong() const
	{
		long val = 0;
		if (getVariable(val)) return val;
		return stdx::atol(getVariable().c_str());
	}
	int asInteger() const
	{
		int val = 0;
		if (getVariable(val)) return val;
		return stdx::atoi(getVariable().c_str());
	}
	bool asBoolean() const
	{
		string str = getVariable();
		if (str.empty()) return false;
		if (stdx::atoi(str.c_str())) return true;
		return strcasecmp(str.c_str(), "true") == 0;
	}
	double asNumber() const
	{
		double val = 0;
		if (getVariable(val)) return val;
		return stdx::atof(getVariable().c_str());
	}
	string asString() const
	{
		return getVariable();
	}
	long asLong(int idx) const
	{
		return get(idx).asLong();
	}
	int asInteger(int idx) const
	{
		return get(idx).asInteger();
	}
	bool asBoolean(int idx) const
	{
		return get(idx).asBoolean();
	}
	double asNumber(int idx) const
	{
		return get(idx).asNumber();
	}
	string asString(int idx) const
	{
		return get(idx).asString();
	}
	int asLong(const string& name) const
	{
		return get(name).asLong();
	}
	int asInteger(const string& name) const
	{
		return get(name).asInteger();
	}
	bool asBoolean(const string& name) const
	{
		return get(name).asBoolean();
	}
	double asNumber(const string& name) const
	{
		return get(name).asNumber();
	}
	string asString(const string& name) const
	{
		return get(name).asString();
	}

public:
	JsonElement operator [] (int idx);
	JsonElement operator [] (const string& name);

	JsonElement& operator = (int val)
	{
		setVariable(val);
		return *this;
	}
	JsonElement& operator = (bool val)
	{
		setVariable(val);
		return *this;
	}
	JsonElement& operator = (long val)
	{
		setVariable(val);
		return *this;
	}
	JsonElement& operator = (double val)
	{
		setVariable(val);
		return *this;
	}
	JsonElement& operator = (const char* val)
	{
		setVariable(val);
		return *this;
	}
	JsonElement& operator = (const string& val)
	{
		setVariable(val);
		return *this;
	}
	JsonElement& operator = (const JsonElement& val)
	{
		holder = val.holder;
		elem = val.elem;
		return *this;
	}
};

static string& operator += (string& str, const JsonElement& obj)
{
	return str += obj.toString();
}

static ostream& operator << (ostream& out, const JsonElement& obj)
{
	return out << obj.toString();
}

static StringCreator& operator << (StringCreator& out, const JsonElement& obj)
{
	return out << obj.toString();
}

static string operator + (const string& str, const JsonElement& obj)
{
	return str + obj.toString();
}

class JsonReflect : public Object
{
public:
	virtual string toString() const;
	virtual string toDocString() const;
	virtual bool fromString(const string& msg);
	virtual bool fromObject(const JsonReflect& obj);
};

template <class DATA_TYPE>
class JsonReflectList : public JsonReflect
{
protected:
	robject(vector<sp<DATA_TYPE>>, vec);

public:
	void clear()
	{
		vec.clear();
	}
	bool empty() const
	{
		return vec.empty();
	}
	size_t size() const
	{
		return vec.size();
	}
	sp<DATA_TYPE> add()
	{
		sp<DATA_TYPE> item = newsp<DATA_TYPE>();

		vec.push_back(item);

		return item;
	}
	void add(sp<DATA_TYPE> val)
	{
		vec.push_back(val);
	}
	sp<DATA_TYPE> get(int idx) const
	{
		return vec[idx];
	}
	const vector<sp<DATA_TYPE>>& getList() const
	{
		return vec;
	}

public:
	string toString() const
	{
		int idx = 0;
		JsonElement res;

		for (auto item : vec) res[idx++].load(item->toString());

		return res.toString();
	}
	string toDocString() const
	{
		return DATA_TYPE().toDocString();
	}
	bool fromString(const string& msg)
	{
		JsonElement src(msg);

		if (src.isNull()) return true;

		CHECK_FALSE_RETURN(src.isArray());

		for (auto item : src)
		{
			sp<DATA_TYPE> dest = newsp<DATA_TYPE>();

			if (item.isString() || item.isArray() || item.isObject())
			{
				CHECK_FALSE_RETURN(dest->fromString(item.toString()));
			}
			else
			{
				CHECK_FALSE_RETURN(dest->fromString(item.getVariable()));
			}

			vec.push_back(dest);
		}

		return true;
	}
	bool fromObject(const JsonReflect& obj)
	{
		return fromString(obj.toString());
	}
};

template <>
class JsonReflectList<int> : public JsonReflect
{
protected:
	robject(vector<int>, vec);

public:
	void clear()
	{
		vec.clear();
	}
	bool empty() const
	{
		return vec.empty();
	}
	void add(int val)
	{
		vec.push_back(val);
	}
	size_t size() const
	{
		return vec.size();
	}
	int get(int idx) const
	{
		return vec[idx];
	}
	const vector<int>& getList() const
	{
		return vec;
	}

public:
	string toString() const
	{
		int idx = 0;
		JsonElement res;

		for (auto item : vec) res[idx++] = item;

		return res.toString();
	}
	string toDocString() const
	{
		return stdx::EmptyString();
	}
	bool fromString(const string& msg)
	{
		JsonElement src(msg);

		if (src.isNull()) return true;

		CHECK_FALSE_RETURN(src.isArray());

		for (auto item : src) vec.push_back(item.asInteger());

		return true;
	}
	bool fromObject(const JsonReflect& obj)
	{
		return fromString(obj.toString());
	}
};

template <>
class JsonReflectList<bool> : public JsonReflect
{
protected:
	robject(vector<bool>, vec);

public:
	void clear()
	{
		vec.clear();
	}
	bool empty() const
	{
		return vec.empty();
	}
	void add(bool val)
	{
		vec.push_back(val);
	}
	size_t size() const
	{
		return vec.size();
	}
	bool get(int idx) const
	{
		return vec[idx];
	}
	const vector<bool>& getList() const
	{
		return vec;
	}

public:
	string toString() const
	{
		int idx = 0;
		JsonElement res;

		for (auto item : vec) res[idx++] = item;

		return res.toString();
	}
	string toDocString() const
	{
		return stdx::EmptyString();
	}
	bool fromString(const string& msg)
	{
		JsonElement src(msg);

		if (src.isNull()) return true;

		CHECK_FALSE_RETURN(src.isArray());

		for (auto item : src) vec.push_back(item.asBoolean());

		return true;
	}
	bool fromObject(const JsonReflect& obj)
	{
		return fromString(obj.toString());
	}
};

template <>
class JsonReflectList<float> : public JsonReflect
{
protected:
	robject(vector<float>, vec);

public:
	void clear()
	{
		vec.clear();
	}
	bool empty() const
	{
		return vec.empty();
	}
	size_t size() const
	{
		return vec.size();
	}
	void add(float val)
	{
		vec.push_back(val);
	}
	float get(int idx) const
	{
		return vec[idx];
	}
	const vector<float>& getList() const
	{
		return vec;
	}

public:
	string toString() const
	{
		int idx = 0;
		JsonElement res;

		for (auto item : vec) res[idx++] = item;

		return res.toString();
	}
	string toDocString() const
	{
		return stdx::EmptyString();
	}
	bool fromString(const string& msg)
	{
		JsonElement src(msg);

		if (src.isNull()) return true;

		CHECK_FALSE_RETURN(src.isArray());

		for (auto item : src) vec.push_back(item.asNumber());

		return true;
	}
	bool fromObject(const JsonReflect& obj)
	{
		return fromString(obj.toString());
	}
};

template <>
class JsonReflectList<double> : public JsonReflect
{
protected:
	robject(vector<double>, vec);

public:
	void clear()
	{
		vec.clear();
	}
	bool empty() const
	{
		return vec.empty();
	}
	size_t size() const
	{
		return vec.size();
	}
	void add(double val)
	{
		vec.push_back(val);
	}
	double get(int idx) const
	{
		return vec[idx];
	}
	const vector<double>& getList() const
	{
		return vec;
	}

public:
	string toString() const
	{
		int idx = 0;
		JsonElement res;

		for (auto item : vec) res[idx++] = item;

		return res.toString();
	}
	string toDocString() const
	{
		return stdx::EmptyString();
	}
	bool fromString(const string& msg)
	{
		JsonElement src(msg);

		if (src.isNull()) return true;

		CHECK_FALSE_RETURN(src.isArray());

		for (auto item : src) vec.push_back(item.asNumber());

		return true;
	}
	bool fromObject(const JsonReflect& obj)
	{
		return fromString(obj.toString());
	}
};

template <>
class JsonReflectList<string> : public JsonReflect
{
protected:
	robject(vector<string>, vec);

public:
	void clear()
	{
		vec.clear();
	}
	bool empty() const
	{
		return vec.empty();
	}
	size_t size() const
	{
		return vec.size();
	}
	void add(const string& val)
	{
		vec.push_back(val);
	}
	string get(int idx) const
	{
		return vec[idx];
	}
	const vector<string>& getList() const
	{
		return vec;
	}

public:
	string toString() const
	{
		int idx = 0;
		JsonElement res;

		for (auto& item : vec) res[idx++] = item;

		return res.toString();
	}
	string toDocString() const
	{
		return stdx::EmptyString();
	}
	bool fromString(const string& msg)
	{
		JsonElement src(msg);

		if (src.isNull()) return true;

		CHECK_FALSE_RETURN(src.isArray());

		for (auto item : src) vec.push_back(item.getVariable());

		return true;
	}
	bool fromObject(const JsonReflect& obj)
	{
		return fromString(obj.toString());
	}
};

#define JsonEntity(clazz) struct clazz : public JsonReflect

#define rarray(type, ...) robject(JsonReflectList<type>, __VA_ARGS__)
#define narray(type, ...) robject(JsonReflectList<type>, __VA_ARGS__, "optional")

////////////////////////////////////////////////////////////////////
#endif